package agenda;

import excecoes.DataInvalida;
import excecoes.FormaDeContatoInvalida;
import excecoes.NomeInvalido;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;

public class Contato implements Comparable<Contato>, Serializable {

	private static final long serialVersionUID = 1L;

	private String nome, endereco;
	private Calendar dataAniversario;
	private List<FormaDeContato> formasDeContato;
	private List<Evento> eventos;


	/**
	 * Cria um contato.
	 * @param nome O nome do contato.
	 * @param endereco O endereco do contato.
	 * @param dataAniversario A data de aniversario do contato.
	 * @param formaContato Uma forma de contato deste contato (Skype, email ou telefone).
	 * @throws NomeInvalido Lanca excecao de NomeInvalido caso o nome do contato seja invalido.
	 * @throws DataInvalida Lanca excecao de DataInvalida caso a data de aniversario do contato seja invalida.
	 */

	public Contato(String nome, String endereco, String dataAniversario, FormaDeContato formaContato)
			throws NomeInvalido, FormaDeContatoInvalida, DataInvalida {

		setNome(nome);
		this.endereco = endereco;

		this.dataAniversario = new GregorianCalendar();
		setDataAniversario(dataAniversario);

		formasDeContato = new ArrayList<FormaDeContato>();
                if (formaContato == null) {
                    throw new FormaDeContatoInvalida("Forma de contato invalida!");
                }
		formasDeContato.add(formaContato);

		eventos = new ArrayList<Evento>();
	}


	/**
	 * Recupera o nome do contato
	 * @return O nome do contato.
	 */

	public String getNome() {
		return nome;
	}

	/**
	 * Altera o nome do contato.
	 * @param nome O novo nome do contato.
	 * @throws NomeInvalido Lanca excecao de NomeInvalido caso o nome do contato seja invalido.
	 */

	public void setNome(String nome) throws NomeInvalido {
		Usuario.validaNome(nome);
		this.nome = nome;
	}

	/**
	 * Recupera o endereco do contato.
	 * @return O endereco do contato.
	 */

	public String getEndereco() {
		return endereco;
	}

	/**
	 * Altera o endereco do contato.
	 * @param endereco O novo endereco do contato.
	 */

	public void setEndereco(String endereco) {
		this.endereco = endereco;
	}

	/**
	 * Recupera a data de aniversario do contato.
	 * @return	A data de aniversario do contato no formato dd/MM/aaaa.
	 */

	public String getDataAniversarioFormatada() {
		return String.format("%02d/%02d", dataAniversario.get(Calendar.DAY_OF_MONTH), (dataAniversario.get(Calendar.MONTH)+1));
	}

	public void setDataAniversario(String novaData) throws DataInvalida {

            if (novaData.length() != 5) {
                throw new DataInvalida("Data de aniversario invalida!");
            }

            if (novaData.charAt(2) != '/') {
                throw new DataInvalida("Data de aniversario invalida!");
            }

            for (int i = 0; i < 5; i++) {
                if (i != 2 && !Character.isDigit(novaData.charAt(i))) {
                    throw new DataInvalida("Data de aniversario invalida!");
                }
            }

            String[] dataQuebrada = novaData.split("/");

            int dia = Integer.parseInt(dataQuebrada[Evento.POSICAO_DIA]);
            int mes = Integer.parseInt(dataQuebrada[Evento.POSICAO_MES])-1;

            if ((mes < dataAniversario.getMinimum(Calendar.MONTH)) || (mes > dataAniversario.getMaximum(Calendar.MONTH))) {
                throw new DataInvalida("Mes invalido!");
            }
            dataAniversario.set(Calendar.MONTH, mes);

            if ((dia < dataAniversario.getMinimum(Calendar.DAY_OF_MONTH)) || (dia > dataAniversario.getMaximum(Calendar.DAY_OF_MONTH))) {
                throw new DataInvalida("Dia invalido!");
            }

            dataAniversario.set(Calendar.DAY_OF_MONTH, dia);
	}

	/**
	 * Recupera a lista de formas de contato do contato.
	 * @return	A lista de formas de contato do contato.
	 */

	public List<FormaDeContato> getFormasDeContato() {
		return formasDeContato;
	}

	/**
	 * Adiciona uma forma de contato ao contato.
	 * @param f A forma de contato a ser adicionada.
	 */

	public void addFormaDeContato(FormaDeContato f) {
		formasDeContato.add(f);
		Collections.sort(formasDeContato);
	}

	/**
	 * Compara dois contatos.
	 * @param c Um contato que sera comparado com este.
	 * @return Zero se os dois contatos possuem nomes iguais, um valor menor que zero se o nome do primeiro contato e
	 * lexicograficamente maior que o nome do segundo ou um valor maior que zero caso contrario.
	 */

	@Override
	public int compareTo(Contato c) {
		return nome.compareTo(c.getNome());
	}

	/**
	 * Verficia se um dos nomes do contato contem a String passada como parametro.
	 * @param parteDoNome Uma parte do nome do contato.
	 * @return true caso algum nome do contato contenha a parte do nome dada e false caso contrario.
	 */

	public boolean nomeContem (String parteDoNome) {

		for (String n : nome.split(" ")) {
			if (n.contains(parteDoNome)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Recupera a lista de eventos cadastrados para este contato
	 * @return A lista de eventos cadastrados para este contato
	 */

	public List<Evento> getEventos() {
		return eventos;
	}

	/**
	 * Adiciona um evento a lista de eventos que este contato vai participar
	 * @param e O evento a ser adicionado
	 */

	public void addEvento(Evento e) {
		int posicao = Agenda.encontraPosicao(e, (ArrayList<Evento>)eventos);
		eventos.add(posicao, e);
	}

	/**
	 * Representa um Contato como String.
	 * @return Uma String que representa um Contato.
	 */

	@Override
	public String toString() {

		String result = getNome() + "\n";
		result += "Endereco: " + getEndereco() + "\n";
		result += "Data de aniversario: " + getDataAniversarioFormatada() + "\n";
		result += "\nForma(s) de contato:";

		for (FormaDeContato f : formasDeContato) {
			result += "\n" + f.toString();
		}

		return result;
	}

}
